package alvarodelrosal.chat.server.modelo;

import alvarodelrosal.chat.server.modelo.chataction.ChatBye;
import alvarodelrosal.chat.server.modelo.chataction.ChatLogin;
import alvarodelrosal.chat.server.modelo.chataction.ChatAction;
import alvarodelrosal.chat.server.modelo.chataction.ChatPrivilegesError;
import alvarodelrosal.chat.server.modelo.chataction.ChatActionsFactory;
import alvarodelrosal.chat.server.modelo.chataction.ChatLoginNotFound;
import alvarodelrosal.chat.server.modelo.chataction.ChatLoginError;
import alvarodelrosal.chat.server.infraestructura.ChatPersistenceManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ChatConnection extends Thread {

    public static final String TOKEN = "<:@:>";
    private Socket client;
    private PrintWriter output;
    private BufferedReader input;
    private ChatUser chatUser = null;
    private String toSend = "";
    private ChatPersistenceManager persistenceManager = ChatPersistenceManager.
            getPersistenceManager();

    public ChatConnection(Socket client) {
        this.client = client;

        try {
            input = new BufferedReader(
                    new InputStreamReader(client.getInputStream()));
        } catch (IOException ex) {
            try {
                client.close();
            } catch (IOException ex1) {
            }
        }

        try {
            output = new PrintWriter(client.getOutputStream(), true);
        } catch (IOException ex) {
            try {
                input.close();
            } catch (IOException ex1) {
            } finally {
                try {
                    client.close();
                } catch (IOException ex1) {
                }
            }
        }
    }

    @Override
    public void run() {
        try {
            String inputRequest = input.readLine();

            ChatBye bye = new ChatBye();

            while (!bye.getName().equals(inputRequest)) {
                if (inputRequest == null) {
                    return;
                } else {
                    String command = getsTheCommand(inputRequest);
                    inputRequest = readsInput(inputRequest, command);
                }
            }
            output.println(bye.doAction(this, new ArrayList()));
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closesALLTheConnections();
        }
    }

    public ChatUser whoAmI() {
        return this.chatUser;
    }

    public void send(String toSend) {
        output.println(toSend);
    }
    
    private void asksForAdminPrivilegesOrGeneratesError(ChatAction action, 
            List<String> parameters) {
        if (chatUser.isAdmin()) {
            executesThe(action, parameters);
        } else {
            output.println(new ChatPrivilegesError().doAction(this,null));
        }
    }

    private boolean asksForLogin(String command) {
        ChatLogin login = new ChatLogin();
        return login.getName().equals(command);
    }

    private void checksIfTheUserIsLogedInOrSendsError(ChatAction action, 
            List<String> parameters) {
        if (userIsLogedIn()) {
            executesIfDoesntNeedAdminPrivileges(action, parameters);
        } else {
            output.println(new ChatLoginError().doAction(this,null));
        }
    }

    private void executesIfDoesntNeedAdminPrivileges(ChatAction action, 
            List<String> parameters) {
        if (action.needsAdminPrivileges()) {
            asksForAdminPrivilegesOrGeneratesError(action, parameters);
        } else {
            executesThe(action, parameters);
        }
    }

    private void executesThe(ChatAction action, List<String> parameters) {
        output.println(action.doAction(this, parameters));
    }

    private void closesALLTheConnections() {
        output.close();
        try {
            input.close();
        } catch (IOException ex1) {
        } finally {
            try {
                client.close();
            } catch (IOException ex1) {
            }
        }
    }

    private String readsInput(String inputRequest, String command) throws IOException {
        ChatAction action = getTheAction(command);

        List<String> parameters = getsTheParameters(inputRequest);
        
        if (action.needsLogin()) {
            checksIfTheUserIsLogedInOrSendsError(action, parameters);
        } else {
            if (asksForLogin(command)) {
                String username = parameters.get(0);
                String password = parameters.get(1);
                if (persistenceManager.existsTheUser(username, password)) {
                    chatUser = persistenceManager.getTheUser(username, password);
                    getsUserDataForAnswer(parameters);
                } else {
                    action = new ChatLoginNotFound();
                }
            }
            executesThe(action, parameters);
        }

        inputRequest = input.readLine();
        return inputRequest;
    }

    private void getsUserDataForAnswer(List<String> parameters) {
        parameters.clear();
        parameters.add(String.valueOf(chatUser.getUserId()));
        parameters.add(chatUser.getName());
        parameters.add(chatUser.getMail());
        parameters.add(String.valueOf(chatUser.isAdmin()));
    }

    private ChatAction getTheAction(String command) {
        ChatActionsFactory inputFactory = new ChatActionsFactory();
        return inputFactory.getFTPAction(command);
    }

    private String getsTheCommand(String inputRequest) {
        return getsThePosition(inputRequest, 0);

    }

    private List<String> getsTheParameters(String inputRequest) {
        List<String> parameters = new ArrayList();
        String[] parametersArray = inputRequest.split(TOKEN);
        for (int position = 1; position < parametersArray.length; position++) {
            parameters.add(parametersArray[position]);
        }
        return parameters;
    }

    private boolean userIsLogedIn() {
        return chatUser != null;
    }

    private String getsThePosition(String inputRequest, int position) {
        return inputRequest.split(TOKEN)[position];
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ChatConnection other = (ChatConnection) obj;
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 41 * hash + (this.client != null ? this.client.hashCode() : 0);
        hash = 41 * hash + (this.output != null ? this.output.hashCode() : 0);
        hash = 41 * hash + (this.input != null ? this.input.hashCode() : 0);
        hash = 41 * hash + (this.chatUser != null ? this.chatUser.hashCode() : 0);
        hash = 41 * hash + (this.toSend != null ? this.toSend.hashCode() : 0);
        hash = 41 * hash + (this.persistenceManager != null ? this.persistenceManager.hashCode() : 0);
        return hash;
    }
    
}
